﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using System.Xml;
using ServiceStack.Redis;

namespace CloudPms.Common.CacheProvider
{
    /// <summary>
    /// 缓存连接池管理器
    /// <para>--------------------------</para>
    /// <para>@Author: cnalexi@gmail.com</para> 
    /// <para>@Create: 2012/10/06</para>  
    /// </summary>
    public class PooledManager
    {
        public static volatile PooledManager _instance;

        private static readonly object _locker = new object();

        private static volatile IList<ClusterUnit> _clusterUnits;
        
        private static readonly ReaderWriterLockSlim barrier = new ReaderWriterLockSlim();

        private const string _baseKey = "0";

        private PooledManager()
        {
        }

        public static PooledManager Instance { 
            get
            {
                if (_instance == null)
                {
                    lock(_locker)
                    {
                        if (_instance == null)
                        {
                            var pm  = new PooledManager();

                            pm.InitPoolManager();

                            _instance = pm;

                            Thread.MemoryBarrier();
                        }
                    }
                
                }

                return _instance;
            }
        }

        /// <summary>
        /// 获取默认的缓存连接管理器
        /// </summary>
        /// <returns></returns>
        public PooledRedisClientManager GetPooledClientManager()
        {
            return GetPooledClientManager(string.Empty);
        }

        /// <summary>
        /// 获取指定酒店编号的缓存连接管理器
        /// </summary>
        /// <param name="hotelId"></param>
        /// <returns></returns>
        public PooledRedisClientManager GetPooledClientManager(string hotelId)
        {
            string id = GetClusterId(hotelId);

            ClusterUnit unit = _clusterUnits.FirstOrDefault(o => o.Id == id);

            if (unit == null)
            {
                throw new InvalidOperationException("缓存连接未能获取，请联系管理员");
            }

            return unit.PooledManager;
                
        }

        /// <summary>
        /// 初始化缓存连接管理池
        /// </summary>
        private void InitPoolManager()
        {
            if (_clusterUnits == null || !_clusterUnits.Any())
            {
                InitRedisHosts();
            }

            if (_clusterUnits == null || !_clusterUnits.Any())
            {
                throw new InvalidOperationException("启动异常：未能成功初始化缓存，请检查缓存服务器配置。");
            }
        }

        /// <summary>
        /// 初始化缓存服务器列表
        /// </summary>
        private void InitRedisHosts()
        {
            _clusterUnits = new List<ClusterUnit>();

            XmlDocument doc = new XmlDocument();

            string root = System.Web.Hosting.HostingEnvironment.MapPath("~") != null
                              ? System.Web.Hosting.HostingEnvironment.MapPath("~") + @"\bin\"
                              : string.Empty;

            if (string.IsNullOrEmpty(root))
            {
                root = (HttpContext.Current != null)
                              ? HttpContext.Current.Request.MapPath("~") + @"\bin\"
                              : string.Empty;
            }

            string path = root + @"redishosts.xml";

            doc.Load(path);

            XmlNodeList hostList = doc.SelectNodes("/RedisHost/cluster");

            foreach (XmlNode node in hostList)
            {
                barrier.EnterWriteLock();

                ClusterUnit cluster = new ClusterUnit();

                cluster.Id = node.Attributes["id"].Value;

                XmlNodeList writeNodes = node.FirstChild.ChildNodes;

                XmlNodeList readOnlyNodes = node.LastChild.ChildNodes;

                string[] readWriteHosts = (from XmlNode hostNode in writeNodes
                                           select hostNode.Attributes["name"].Value).ToArray();

                string[] readonlyHosts = (from XmlNode hostNode in readOnlyNodes
                                          select hostNode.Attributes["name"].Value).ToArray();

                cluster.PooledManager = new PooledRedisClientManager(readWriteHosts, readonlyHosts, new RedisClientManagerConfig
                {
                    MaxWritePoolSize = 50,
                    MaxReadPoolSize = 50,
                    AutoStart = true
                });

                _clusterUnits.Add(cluster);

                barrier.ExitWriteLock();
            }
        }

        /// <summary>
        /// 判断缓存服务器是否连接
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        public bool Ping(RedisClient client)
        {
            try
            {
                if (client.Ping())
                {
                    return true;
                }
            }
            catch
            {
                return false;
            }
            return false;
        }

        /// <summary>
        /// 获取缓存标识
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private string GetClusterId(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return _baseKey;
            }

            //Math.Abs(key.GetHashCode());
            int keyIndex = int.Parse(key.Substring(key.Length-3));

            if (_clusterUnits == null || _clusterUnits.Count <= 0)
            {
                throw new InvalidOperationException("ClusterUnits is null.获取缓存标识失败，请立即联系管理员处理");
            }

            int value = _clusterUnits.Count;

            return (keyIndex % value).ToString(CultureInfo.InvariantCulture);
        }

    }
}
